package complier.craft;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;

/**
 * 一个简单的脚本解释器。
 * 所支持的语法，请参见SimpleParser.java
 * <p>
 * 运行脚本：
 * 在命令行下，键入：java SimpleScript
 * 则进入一个REPL界面。你可以依次敲入命令。比如：
 * > 2+3;
 * > int age = 10;
 * > int b;
 * > b = 10*2;
 * > age = age + b;
 * > exit();  //退出REPL界面。
 * <p>
 * 你还可以使用一个参数 -v，让每次执行脚本的时候，都输出AST和整个计算过程。
 */
public class SimpleScript {
    private static HashMap<String, Integer> variables = new HashMap<>();
    private static boolean verbose = true;// 是否打开调试语句

    public static void main(String[] args) throws IOException {
        // 是否打开调试模式 -v
        if (args.length > 0 && args[0].equals("-v")) {
            verbose = true;
            System.out.println("verbose mode");
        }
        System.out.println("Welcome To Simple Script Language!");

        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        SimpleScript script = new SimpleScript();
        SimpleParser parser = new SimpleParser();

        String scriptText = "";
        System.out.println("\n>");
        while (true) {
            String line = reader.readLine();
            // exit 退出
            if (line.equals("exit();")) {
                System.out.println("good bye!");
                break;
            }
            // 读命令
            scriptText += line + "\n";
            try {
                if (line.endsWith(";")) {
                    ASTNode tree = parser.parse(scriptText);

                    if (verbose == true) {
                        parser.dumpAST(tree, "");
                    }
                    Integer result = script.evaluate(tree, "");
                    System.out.println(result + "\n>");
                    scriptText = "";
                }

            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("error:" + e.getLocalizedMessage());
                System.out.println("\n>");
                scriptText = "";
            }

        }

    }

    /**
     * 遍历AST，计算值。
     *
     * @param node
     * @param indent
     * @return
     * @throws Exception
     */
    private Integer evaluate(ASTNode node, String indent) throws Exception {
        Integer result = null;
        if (verbose) {
            System.out.println(indent + "Calculating:" + node.getType());
        }

        switch (node.getType()) {
            case Program:
                for (ASTNode child : node.getChildren()) {
                    result = evaluate(child, indent + "\t");
                }
                break;
            case Additive:
                ASTNode child1 = node.getChildren().get(0);
                int av1 = evaluate(child1, indent + "\t");
                ASTNode child2 = node.getChildren().get(1);
                int av2 = evaluate(child2, indent + "\t");
                if (node.getText().equals("+")) {
                    result = av1 + av2;
                } else {
                    result = av1 - av2;
                }
                break;
            case Multiplicative:
                ASTNode node1 = node.getChildren().get(0);
                int mv1 = evaluate(node1, indent + "\t");
                ASTNode node2 = node.getChildren().get(1);
                int mv2 = evaluate(node2, indent + "\t");
                if (node.getText().equals("*")) {
                    result = mv1 * mv2;
                } else {
                    result = mv1 - mv2;
                }
                break;
            case IntLiteral:
                result = Integer.parseInt(node.getText());
                break;
            // 添加 Identifier AssignmentStmt IntDeclaration
            case Identifier:
                String varName = node.getText();
                if (variables.containsKey(varName)) {
                    Integer value = variables.get(varName);
                    if (value != null) {
                        result = value.intValue();
                    } else {
                        throw new Exception("variable " + varName + " has not been set any value");
                    }
                } else {
                    throw new Exception("unknown variable: " + varName);
                }
                break;
            case AssignmentStmt:
                varName = node.getText();
                if (!variables.containsKey(varName)) {
                    throw new Exception("unknown variable: " + varName);
                }
            case IntDeclaration:
                varName = node.getText();
                Integer value = null;

                if (node.getChildren().size() > 0) {
                    ASTNode child = node.getChildren().get(0);
                    result = evaluate(child, indent + "\t");
                    value = result.intValue();
                }
                variables.put(varName, value);
                break;
        }
        if (verbose) {
            System.out.println(indent + "Result:" + result);
            if (indent.equals("")) { //顶层表达式
                if (node.getType() == ASTNodeType.AssignmentStmt || node.getType() == ASTNodeType.Identifier) {
                    System.out.println(node.getText() + ":" + result);
                } else if (node.getType() != ASTNodeType.Program) {
                    System.out.println(result);
                }
            }
        }

        return result;
    }


}
